﻿using DotNetty.Codecs;
using DotNetty.Codecs.Compression;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using DotNetty.Transport.Libuv;
using FileCommon.Codecs;
using FileCommon.Extensions;
using FileServer.Core.Configuration;
using FileServer.Core.Server.Handlers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace FileServer.Core.Server;

public class DotNettyFileServerListener : IFileServerListener
{
    private readonly ILogger<DotNettyFileServerListener> _logger;
    private readonly FileServerOption _fileServerOption;
    private IChannel m_boundChannel;
    private IEventLoopGroup m_bossGroup;
    private IEventLoopGroup m_workerGroup;


    public DotNettyFileServerListener(ILogger<DotNettyFileServerListener> logger,
        IOptions<FileServerOption> fileServerOption)
    {
        _logger = logger;
        _fileServerOption = fileServerOption.Value;
    }

    public async Task Listen()
    {
        var bootstrap = new ServerBootstrap();
        if (_fileServerOption.UseLibuv)
        {
            var dispatcher = new DispatcherEventLoopGroup();
            m_bossGroup = dispatcher;
            m_workerGroup = new WorkerEventLoopGroup(dispatcher);
            bootstrap.Channel<TcpServerChannel>();
        }
        else
        {
            m_bossGroup = new MultithreadEventLoopGroup(1);
            m_workerGroup = new MultithreadEventLoopGroup();
            bootstrap.Channel<TcpServerSocketChannel>();
        }

        // todo SSL ?

        bootstrap
            .Option(ChannelOption.SoBacklog, _fileServerOption.SoBacklog)
            .Option(ChannelOption.TcpNodelay, true)
            .Group(m_bossGroup, m_workerGroup)
            .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
            {
                var pipeline = channel.Pipeline;
                pipeline.AddLast(new LengthFieldPrepender(4));
                pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4, 0, 4));
                pipeline.AddLast(ZlibCodecFactory.NewZlibEncoder(ZlibWrapper.Gzip));
                pipeline.AddLast(ZlibCodecFactory.NewZlibDecoder(ZlibWrapper.Gzip));
                // 编解码器
                pipeline.AddLast(new ObjectDecoder());
                pipeline.AddLast(new ObjectEncoder());
                // 文件传输处理器
                pipeline.AddLast(new FileTransferServerHandler(_fileServerOption.RootPath));
            }))
            ;

        try
        {
            m_boundChannel = await bootstrap.BindAsync(_fileServerOption.Port);
            _logger.LogInformation(
                "The file server listener started successfully, the listening Port: {0}",
                _fileServerOption.Port);
        }
        catch (Exception ex)
        {
            _logger.LogException(ex);
            throw;
        }
    }

    public async void Dispose()
    {
        if (m_boundChannel != null) await m_boundChannel.CloseAsync();
        if (m_bossGroup != null)
            await m_bossGroup.ShutdownGracefullyAsync();
        if (m_workerGroup != null)
            await m_workerGroup.ShutdownGracefullyAsync();
    }
}